package cn.tedu.mall.order.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@DubboService
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {
    //dubbo调用product模块减少库存
    @DubboReference
    private IForOrderSkuService dubboSkuService;
    @Autowired
    private IOmsCartService omsCartService;

    @Autowired
    private OmsOrderMapper omsOrderMapper;

    @Autowired
    private OmsOrderItemMapper omsOrderItemMapper;

    //新增订单的方法，
    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {
        // 第一部分: 收集信息,准备数据
        // 先收集订单信息,实例化OmsOrder对象
        OmsOrder order=new OmsOrder();
        // 将参数OrderAddDTO同名属性赋值给order对象
        BeanUtils.copyProperties(orderAddDTO,order);
        // 参数orderAddDTO中信息并不完整,有些信息需要我们生成或收集
        // 编写一个专门收集信息的方法,完成信息收集的工作
        loadOrder(order);
        // 到此为止,order对象的所有属性就收集完毕了
        // 下面要将参数orderAddDTO中包含的订单项的集合:orderItems进行信息的收集工作
        List<OrderItemAddDTO> orderItems=orderAddDTO.getOrderItems();
        if(orderItems == null  ||  orderItems.isEmpty()){
            // 如果参数orderAddDTO中包含的订单项集合是null或没有元素
            // 抛出异常终止程序
            throw new CoolSharkServiceException(
                    ResponseCode.BAD_REQUEST,"订单中至少要包含一件商品");
        }
        // 我们最终目标是将订单项信息保存到数据库
        // 数据库中编写的方法参数类型是List<OmsOrderItem>
        // 所以我们要遍历orderItems集合中的每个元素,转换为OmsOrderItem
        // 然后再保存再List<OmsOrderItem>类型的集合中才能完成订单项的新增
        // 先实例化List<OmsOrderItem>的集合
        List<OmsOrderItem> omsOrderItems=new ArrayList<>();
        // 遍历DTO中的集合
        for(OrderItemAddDTO addDTO : orderItems){
            // 实例化目标对象OmsOrderItem
            OmsOrderItem omsOrderItem=new OmsOrderItem();
            // 将addDTO的同名属性赋值到omsOrderItem对象中
            BeanUtils.copyProperties(addDTO,omsOrderItem);
            // 将addDTO对象没有的id属性和orderId属性赋值
            // id属性还是通过Leaf获取
            Long itemId=IdGeneratorUtils.getDistributeId("order_item");
            omsOrderItem.setId(itemId);
            // 订单id直接从order对象中获取
            omsOrderItem.setOrderId(order.getId());
            omsOrderItems.add(omsOrderItem);
            // 第二部分: 执行新增订单相关数据库操作指令
            // 1.减少库存
            // 当前正在遍历的对象是一个包含skuId和购买数量的对象
            // 先获取skuId
            Long skuId=omsOrderItem.getSkuId();
            log.error("sukId获取失败:{}",omsOrderItem);
            // dubbo调用product模块写好的减少库存的方法
            int num = dubboSkuService.
                    reduceStockNum(skuId, omsOrderItem.getQuantity());
            // 判断num是否为0
            if(num == 0){
                log.error("商品库存不足,skuId:{}",skuId);
                // 抛出异常,终止程序,触发seata分布式事务回滚
                throw new CoolSharkServiceException(
                        ResponseCode.BAD_REQUEST,"库存不足");
            }
            // 2.删除用户在购物车中勾选的商品信息
            OmsCart omsCart=new OmsCart();
            omsCart.setUserId(order.getUserId());
            omsCart.setSkuId(skuId);
            // 执行删除
            omsCartService.removeUserCarts(omsCart);
        }
        // 3.执行新增订单
        omsOrderMapper.insertOrder(order);
        // 4.执行新增订单项(批量新增集合中所有订单项数据)
        omsOrderItemMapper.insertOrderItemList(omsOrderItems);
        // 第三部分: 准备返回值,返回给前端
        // 实例化返回值类型对象
        OrderAddVO addVO=new OrderAddVO();
        addVO.setId(order.getId());
        addVO.setSn(order.getSn());
        addVO.setCreateTime(order.getGmtCreate());
        addVO.setPayAmount(order.getAmountOfActualPay());
        // 最后千万别忘了返回!!!!
        return addVO;
    }

    private void loadOrder(OmsOrder order) {
        Long id = IdGeneratorUtils.getDistributeId("order");
        order.setId(id);
        order.setSn(UUID.randomUUID().toString());
        if (order.getUserId() == null){
            order.setUserId(getUserId());
        }
        if (order.getState() == null){
            order.setState(0);
        }
        LocalDateTime now = LocalDateTime.now();
        order.setGmtOrder(now);
        order.setGmtCreate(now);
        order.setGmtModified(now);
        BigDecimal price = order.getAmountOfOriginalPrice();
        BigDecimal freight = order.getAmountOfFreight();
        BigDecimal discount = order.getAmountOfDiscount();
        BigDecimal actualPay=price.subtract(discount).add(freight);
        order.setAmountOfActualPay(actualPay);

    }

    //根据订单id修改订单状态
    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        //先实例化OmsOrder对象
        OmsOrder order = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO,order);
        //调用动态修改方法，按给定的id作为条件，修改订单状态为state值
        omsOrderMapper.updateOrderById(order);
    }

    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        //方法开始需要在运行前先判断用户给定的时间范围
        //判断orderListTimeDTO中开始时间和结束时间是否有效，编写专门的方法
        validateTimes(orderListTimeDTO);
        //设置分页
        PageHelper.startPage(orderListTimeDTO.getPage(),orderListTimeDTO.getPageSize());
        //将用户id赋值到orderListTimeDTO中
        orderListTimeDTO.setUserId(getUserId());
        //执行查询
        List<OrderListVO> list = omsOrderMapper.selectOrdersBetweenTimes(orderListTimeDTO);
        return JsonPage.restPage(new PageInfo<>(list));
    }
    private void validateTimes(OrderListTimeDTO orderListTimeDTO){
        //获取用户提供的开始时间结束时间
        LocalDateTime start = orderListTimeDTO.getStartTime();
        LocalDateTime end = orderListTimeDTO.getEndTime();
        if (start == null || end == null){
            //开始时间设置为当前时间一个月前
            start = LocalDateTime.now().minusMonths(1);
            end = LocalDateTime.now();
            orderListTimeDTO.setStartTime(start);
            orderListTimeDTO.setEndTime(end);
        }else {
            //start和end都非空
            //判断end<start，抛异常
            if (end.isBefore(start)){
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"结束时间应大于开始时间");
            }
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }

    public CsmallAuthenticationInfo getUserInfo(){
        //获取spring Security上下文
        UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        if (authenticationToken == null){
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"您还未登录");
        }
        //从spring security上下文获取用户信息
        CsmallAuthenticationInfo csmallAuthenticationInfo = (CsmallAuthenticationInfo) authenticationToken.getCredentials();
        return csmallAuthenticationInfo;
    }
    //业务逻辑层实际开发时，大部分都是需要用户id而已，所有直接编写方法，返回用户id
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
